package wxdb.metadata;

import static java.sql.Types.INTEGER;
import static java.sql.Types.VARCHAR;

import java.io.*;
import java.util.*;

import wxdb.index.btree.BTreeIndex;
import wxdb.query.Plan;
import wxdb.query.Predicate;
import wxdb.query.TablePlan;
import wxdb.query.Term;
import wxdb.record.*;
import wxdb.ui.Global;

public class TableMgr {
	
	public static TableInfo getMeta(int dbid, String tableName){
		String fileName = "metadata/" + Global.findDBName(dbid) + tableName + ".ti";
		try{
			FileInputStream fis = new FileInputStream(fileName);
			ObjectInputStream ois = new ObjectInputStream(fis);
			return (TableInfo)ois.readObject();
		}catch(ClassNotFoundException cnfe){
			cnfe.printStackTrace();
		}catch(IOException ioe){
			System.out.println("Table Not Exist!");
		}
		return null;
	}
	
	public static void writeMeta(int dbid, String tableName, TableInfo ti){
		String fileName = "metadata/" + Global.findDBName(dbid) + tableName + ".ti";
		try{
			FileOutputStream fos = new FileOutputStream(fileName);
			ObjectOutputStream ois = new ObjectOutputStream(fos);
			ois.writeObject(ti);
		}catch(IOException ioe){
			ioe.printStackTrace();
		}
	}
	
	public static HashMap<String,ArrayList> groupBy(HashMap<String,ArrayList> hm , String field){
		HashMap<String,ArrayList> result = new HashMap<String,ArrayList>();
		Set<String> keys = hm.keySet();
		for(String key : keys)
			result.put(key, new ArrayList());
		
		
		while(hm.get(field).size()!=0){
			Object o = hm.get(field).remove(0);
			result.get(field).add(o);
			for(String key : keys){
				if(!key.equals(field))
					result.get(key).add(hm.get(key).remove(0));
			}
			
			boolean changed = false;
			do{
				changed = false;
				int i =0;
				for(Object obj : hm.get(field)){
					if(obj.equals(o)){
						changed = true;
						result.get(field).add(hm.get(field).remove(i));
						for(String key : keys){
							if(!key.equals(field))
								result.get(key).add(hm.get(key).remove(i));
						}
					}
					if(changed == true)
						break;
					else
						i++;
				}
			}while(changed==true);
		}
		return result;
	}
	
	public static HashMap<String,ArrayList> orderBy(HashMap<String,ArrayList> hm , String field){
		HashMap<String,ArrayList> result = new HashMap<String,ArrayList>();
		Set<String> keys = hm.keySet();
		for(String key : keys)
			result.put(key, new ArrayList());
		
		int indexremove = 0;
		int minivalue = 0;
		while(hm.get(field).size()!=0){
			for(int i=0 ; i<hm.get(field).size() ;i++){
				if(i==0){
					indexremove = 0;
					minivalue =((Integer)hm.get(field).get(0)).intValue();
				}else{
					if(((Integer)hm.get(field).get(i)).intValue()<=minivalue){
						indexremove = i;
						minivalue = ((Integer)hm.get(field).get(i)).intValue();
					}
				}
			}
			for(String key : keys)
				result.get(key).add(hm.get(key).remove(indexremove));
		}
		return result;
	}
	
	public static HashMap<String,ArrayList> productTable(HashMap<String,ArrayList> hm1,HashMap<String,ArrayList> hm2){
		Set<String> hm1set = hm1.keySet();
		Set<String> hm2set = hm2.keySet();
		HashMap<String,ArrayList> result = new HashMap<String,ArrayList>();
		int hm1recordnum = 0;
		int hm2recordnum = 0;
		
		for(String hm1key : hm1set){
			result.put(hm1key, new ArrayList());
			ArrayList hm1values = hm1.get(hm1key);
			hm1recordnum = hm1values.size();
		}
		for(String hm2key : hm2set){
			result.put(hm2key, new ArrayList());	
			ArrayList hm2values = hm2.get(hm2key);
			hm2recordnum = hm2values.size();
		}
		
		for(int i = 0 ; i<hm1recordnum ; i++){
			for(int j = 0; j<hm2recordnum ; j++){
				for(String hm1key : hm1set){
					Object o = hm1.get(hm1key).get(i);
					result.get(hm1key).add(o);
				}
				for(String hm2key : hm2set){
					Object o = hm2.get(hm2key).get(j);
					result.get(hm2key).add(o);
				}
			}
		}
		return result;
	}
	
	public static ArrayList projectTable(String filedname, HashSet<RID> rids,String tbname){
		TableInfo t = TableMgr.getMeta(Global.currentdb, tbname);
		ArrayList res = new ArrayList();
		for(RID rid : rids){
			RecordPage rp = new RecordPage(rid.getPid(),t);
			rp.moveTo(rid.getSlotNum());
   			Schema sch = t.getSchema();
	   	  	if(INTEGER ==  sch.getType(filedname))			  
	   	  		res.add(rp.getInt(filedname));
	   	  	if(VARCHAR == sch.getType(filedname))
	   	  		res.add(rp.getString(filedname));
	   	}     
		return res;
	}
	
	public static HashSet<RID> selectTable (String tblname, Predicate pred){

		HashSet<RID> resultset = new HashSet<RID>();
		TableInfo ti = TableMgr.getMeta(Global.currentdb, tblname);
		if(ti.getPageNum()==0)
			return resultset;
 	   		  
		ArrayList<Term> terms = pred.getTerms();
		if(terms.size()==0){
			BTreeIndex bin = new BTreeIndex();
  		  	for(String fild : ti.getSchema().getFields()){
  		  		bin = IndexInfoMgr.getIndexInfo(Global.currentdb,tblname, fild);
  		  		break;
  		  	}
  		  	bin.moveToHead();
  		  	while(bin.nextSlot()){
  		  		resultset.add(bin.getRID());
  		  	}
  		  	return resultset;
  	  	}
		String andor = pred.getAndOr();
  	  	ArrayList<Plan> plans = new ArrayList<Plan>();
  	  	for(int i=0;i<terms.size();i++){
  	  		String fldn = terms.get(i).getFieldname();
  	  		Plan p = new TablePlan(tblname,fldn);
  	  		plans.add(p);
  	  	}
  	  	
  	  	ArrayList<ArrayList<RID>> ridlist = new ArrayList<ArrayList<RID>>(plans.size());
  	  	for(int i=0;i<plans.size();i++)
  	  		ridlist.add(new ArrayList<RID>());
  	  	
  	  	for(int i=0 ;i<plans.size();i++){
  	  		String fldname = ((TablePlan)plans.get(i)).getFieldName();
  	  		BTreeIndex bindex = IndexInfoMgr.getIndexInfo(Global.currentdb,tblname, fldname);
  	  		Term t = terms.get(i);
  	  		
  	  		if(t.getBinOper().equals("GT")){
  	  			bindex.beforeFirst(t.getValue());
  	  			while(bindex.next())
  	  				;
  	  			do{
  	  				ridlist.get(i).add(bindex.getRID());
  	  			}while(bindex.nextSlot());				  
  	  		}
  	  		else if(t.getBinOper().equals("GE")){
  	  			bindex.beforeFirst(t.getValue());
  	  			while(bindex.nextSlot()){
  	  				ridlist.get(i).add(bindex.getRID());
  	  			}
  	  		}
  	  		else if(t.getBinOper().equals("LT")){
  	  			bindex.beforeFirst(t.getValue());
  	  			do{
  	  				ridlist.get(i).add(bindex.getRID());
  	  			}while(bindex.preSlot());
  	  		}
  	  		else if(t.getBinOper().equals("LE")){
  	  			bindex.beforeFirst(t.getValue());
  	  			while(bindex.next()){
  	  				ridlist.get(i).add(bindex.getRID());
  	  			}
  	  			bindex.beforeFirst(t.getValue());
  	  			do{
  	  				ridlist.get(i).add(bindex.getRID());
  	  			}while(bindex.preSlot());
  	  		}
  	  		else if(t.getBinOper().equals("EQ")){
  	  			bindex.beforeFirst(t.getValue());
  	  			while(bindex.next()){
  	  				ridlist.get(i).add(bindex.getRID());
  	  			}
  	  		}
  	  	}
  	  	//and or
  	  	HashSet<RID> orset = new HashSet<RID>();
  	  	HashSet<RID> andset = new HashSet<RID>();
  	  	HashSet<RID> noandorset = new HashSet<RID>();
  	  	
  	  	
  	  	if(andor.equals("and")){
  	  		for(ArrayList<RID> al : ridlist){
  	  			if(andset.size()==0){
  	  				andset.addAll(al);
  	  				continue;
  	  			}		  
  	  			for(RID rid : al){
  	  				for(RID res : andset){
  	  					if(res.equals(rid))
  	  						noandorset.add(res);
  	  				}
  	  			}
  	  			andset.clear();
  	  			andset.addAll(noandorset);
  	  			noandorset.clear();
  	  		}
  	  		resultset.addAll(andset);
  	  	}else if(andor.equals("or")){ 
  	  		for(ArrayList<RID> al : ridlist)
  	  			orset.addAll(al);
  	  		resultset.addAll(orset);
  	  	}else{
  	  		noandorset.clear();
  	  		noandorset.addAll(ridlist.get(0));
  	  		resultset.addAll(noandorset);
  	  	}
  	  	return resultset;
	}

	public static HashMap<String, ArrayList> countGroup(
			HashMap<String, ArrayList> r, String computefield, String asfield) {
		// TODO Auto-generated method stub
		
		HashMap<String,ArrayList> result = new HashMap<String,ArrayList>();
		result.put(Global.group_by_field, new ArrayList());
		result.put(Global.asfield, new ArrayList());
		
		ArrayList bylist = r.get(Global.group_by_field);
		ArrayList targetlist = r.get(Global.computefield);
		String currentname = bylist.get(0).toString();
		int count = 0;
		for(int i=0;i<bylist.size();i++){
			if(!currentname.equals(bylist.get(i).toString())){
				result.get(Global.group_by_field).add(currentname);
				result.get(Global.asfield).add(count);
				currentname = bylist.get(i).toString();
				count = 1;
			}else{
				count++;
			}
		}
		result.get(Global.group_by_field).add(currentname);
		result.get(Global.asfield).add(count);
		return result;
	}

	public static HashMap<String, ArrayList> minGroup(
			HashMap<String, ArrayList> r, String computefield, String asfield) {
		// TODO Auto-generated method stub
		HashMap<String,ArrayList> result = new HashMap<String,ArrayList>();
		result.put(Global.group_by_field, new ArrayList());
		result.put(Global.asfield, new ArrayList());
		
		ArrayList bylist = r.get(Global.group_by_field);
		ArrayList targetlist = r.get(Global.computefield);
		String currentname = bylist.get(0).toString();
		int min = ((Integer)targetlist.get(0)).intValue();
		for(int i=0;i<bylist.size();i++){
			if(!currentname.equals(bylist.get(i).toString())){
				result.get(Global.group_by_field).add(currentname);
				result.get(Global.asfield).add(min);
				currentname = bylist.get(i).toString();
				min = ((Integer)targetlist.get(i)).intValue();
			}else{
				if(((Integer)targetlist.get(i)).intValue()<=min)
					min = ((Integer)targetlist.get(i)).intValue();
			}
		}
		result.get(Global.group_by_field).add(currentname);
		result.get(Global.asfield).add(min);
		
		return result;
	}

	public static HashMap<String, ArrayList> maxGroup(
			HashMap<String, ArrayList> r, String computefield, String asfield) {
		// TODO Auto-generated method stub
		HashMap<String,ArrayList> result = new HashMap<String,ArrayList>();
		result.put(Global.group_by_field, new ArrayList());
		result.put(Global.asfield, new ArrayList());
		
		ArrayList bylist = r.get(Global.group_by_field);
		ArrayList targetlist = r.get(Global.computefield);
		String currentname = bylist.get(0).toString();
		int max = ((Integer)targetlist.get(0)).intValue();
		for(int i=0;i<bylist.size();i++){
			if(!currentname.equals(bylist.get(i).toString())){
				result.get(Global.group_by_field).add(currentname);
				result.get(Global.asfield).add(max);
				currentname = bylist.get(i).toString();
				max = ((Integer)targetlist.get(i)).intValue();
			}else{
				if(((Integer)targetlist.get(i)).intValue()>=max)
					max = ((Integer)targetlist.get(i)).intValue();
			}
		}
		result.get(Global.group_by_field).add(currentname);
		result.get(Global.asfield).add(max);
		return result;
	}

	public static HashMap<String, ArrayList> avgGroup(
			HashMap<String, ArrayList> r, String computefield, String asfield) {
		// TODO Auto-generated method stub
		HashMap<String,ArrayList> result = new HashMap<String,ArrayList>();
		result.put(Global.group_by_field, new ArrayList());
		result.put(Global.asfield, new ArrayList());
		
		ArrayList bylist = r.get(Global.group_by_field);
		ArrayList targetlist = r.get(Global.computefield);
		String currentname = bylist.get(0).toString();
		
		int sum = 0;
		int count = 0;
		
		for(int i=0;i<bylist.size();i++){
			if(!currentname.equals(bylist.get(i).toString())){
				result.get(Global.group_by_field).add(currentname);
				result.get(Global.asfield).add(sum/count);
				currentname = bylist.get(i).toString();
				sum = ((Integer)targetlist.get(i)).intValue();
				count = 1;
			}else{			
				sum += ((Integer)targetlist.get(i)).intValue();
				count ++;
			}
		}
		result.get(Global.group_by_field).add(currentname);
		result.get(Global.asfield).add(sum/count);
		return result;
	}

	public static HashMap<String, ArrayList> sumGroup(
			HashMap<String, ArrayList> r, String computefield, String asfield) {
		// TODO Auto-generated method stub
		HashMap<String,ArrayList> result = new HashMap<String,ArrayList>();
		result.put(Global.group_by_field, new ArrayList());
		result.put(Global.asfield, new ArrayList());
		
		ArrayList bylist = r.get(Global.group_by_field);
		ArrayList targetlist = r.get(Global.computefield);
		String currentname = bylist.get(0).toString();
		
		int sum = 0;
		
		for(int i=0;i<bylist.size();i++){
			if(!currentname.equals(bylist.get(i).toString())){
				result.get(Global.group_by_field).add(currentname);
				result.get(Global.asfield).add(sum);
				currentname = bylist.get(i).toString();
				sum = ((Integer)targetlist.get(i)).intValue();
			}else{			
				sum += ((Integer)targetlist.get(i)).intValue();
			}
		}
		result.get(Global.group_by_field).add(currentname);
		result.get(Global.asfield).add(sum);
		return result;
	}
}
